package com.iqiyi.pps.epg.web.timer;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.iqiyi.kiwi.utils.Configuration;
import com.iqiyi.pps.epg.api.model.web.program.TProgram;
import com.iqiyi.pps.epg.core.model.channel.ChannelBase;
import com.iqiyi.pps.epg.core.model.channel.ChannelConfig;
import com.iqiyi.pps.epg.core.model.program.ProgramDataExpired;
import com.iqiyi.pps.epg.core.rpc.qipu.impl.QipuManager;
import com.iqiyi.pps.epg.core.service.channel.ChannelService;
import com.iqiyi.pps.epg.core.service.program.ProgramService;
import com.iqiyi.pps.epg.core.utils.ApiHttpRequestUtil;
import com.iqiyi.pps.epg.core.utils.ApiQipuUtil;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Created by lemuelwang on 2015/11/10.
 */
public class ProgramTempRecycleTrigger {
    private static Logger logger = LoggerFactory.getLogger(ProgramTempRecycleTrigger.class);

    @Autowired
    private ChannelService channelService;
    @Autowired
    private ProgramService programService;
    @Autowired
    private QipuManager qipuManager;

    private ConcurrentLinkedQueue<ChannelBase> fetchingQueue;
    private ConcurrentLinkedQueue<ProgramDataExpired> deleteQueue;

  //  public static final String QIPU_ENTITY_URL = Configuration.getString("qipu.entity.url");
//    public static final String QIPU_ENTITY_URL = "http://qipu-test.qiyi.domain/int/entity/nocache/@.json";
    public static final String LIVE_EPISODE_SUMMARY = "live_episode_summary";
    public static final String PROGRAM_START_PLAY_TIME = "start_play_time";
    public static final String FLAG_TYPE = "program_recycle";

    public static final int STATE_RUNNING = 1;
    public static final int STATE_IDLE = 0;
    public static final int STATE_FORCESTOP = 2;

    private int state = 0;

    public void task() {
        try {
            List<ChannelConfig> flag = channelService.getConfig(FLAG_TYPE);
            if (null == flag || flag.size() == 0) {
                return;
            }

            String flagVal = flag.get(0).getVal();

            if(!needRunning(flagVal)){
                return;
            }

            logger.info("[ProgramTempRecycleTrigger] task start");

            switch(flagVal){
                case "0":
                    state = STATE_IDLE;
                    break;
                case "1":
                    state = STATE_RUNNING;
                    fetchingTask();
                    break;
                case "2":
                    state = STATE_RUNNING;
                    deleteTask();
                    break;
                case "3":
                    state = STATE_FORCESTOP;
                    break;
            }

            logger.info("[ProgramTempRecycleTrigger] task end");
        }catch (Exception e){
            logger.info("[ProgramTempRecycleTrigger] task exception {}", e);
        }

    }

    private void fetchingTask(){
        List<ChannelBase> channelList = getChannelList();

        if(channelList.size() > 0){

            fetchingQueue = new ConcurrentLinkedQueue<>(channelList);

            logger.info("[ProgramTempRecycleTrigger] start fetching program");
            ExecutorService exec = Executors.newFixedThreadPool(3);
            for (int i = 0; i < 3; i++) {
                exec.execute(new FetchingExecutor());
            }
            exec.shutdown();
            try {
                while (!exec.awaitTermination(10, TimeUnit.SECONDS)) ;
            } catch (Exception e) {
                logger.info("[ProgramTempRecycleTrigger][task][awaitTermination][exception={}]", e);
            }
            logger.info("[ProgramTempRecycleTrigger] end fetching program");

        }

    }

    private void deleteTask(){
        List<ProgramDataExpired> programList = programService.getExpiredProgramList();

        if(programList.size() > 0){
            deleteQueue = new ConcurrentLinkedQueue<>(programList);
            logger.info("[ProgramTempRecycleTrigger] start delete program");
            ExecutorService exec = Executors.newFixedThreadPool(1);
            for (int i = 0; i < 1; i++) {
                exec.execute(new DeleteExecutor());
            }
            exec.shutdown();
            try {
                while (!exec.awaitTermination(10, TimeUnit.SECONDS)) ;
            } catch (Exception e) {
                logger.info("[ProgramTempRecycleTrigger][task][awaitTermination][exception={}]", e);
            }
            logger.info("[ProgramTempRecycleTrigger] end delete program");
        }

    }

    private synchronized boolean needRunning(String flag) {
        return state == 0 || flag.equals("0") || flag.equals("3");
    }

    private List<ChannelBase> getChannelList(){
        List<Criterion> filters = new ArrayList<>();
        filters.add(Restrictions.or(
                        Restrictions.eq("liveType", 1),
                        Restrictions.eq("liveType", 3)
                )
        );
        // TODO remove isEnable restriction
//        filters.add(Restrictions.eq("isEnable", 1));
        return channelService.findChannelBaseList(filters);
    }

    private long getPreviousNDays(int n){
        Calendar ca = Calendar.getInstance();
        ca.setTimeInMillis(System.currentTimeMillis());
        ca.add(Calendar.DATE, 0 - n);
        return ca.getTimeInMillis();
    }

    private ProgramDataExpired transferProgram(ChannelBase channel, JSONObject entity){
        ProgramDataExpired result = new ProgramDataExpired();
        result.setFatherId(channel.getId());
        result.setFatherQipuId(channel.getQipuId());
        result.setFatherOldQipuId(channel.getOldQipuId());
        result.setQipuId(entity.getLongValue("entity_id"));
        result.setDesc(entity.getString("desc"));
        result.setStartPlayTime(new Timestamp(entity.getLongValue(PROGRAM_START_PLAY_TIME)));
        result.setOrder(entity.getLongValue("order"));
        result.setIsDeleted(0);
        result.setId(0);

        logger.info("[ProgramTempRecycleTrigger][ProgramName={}][qipuId={}][startPlayTime={}][order={}]",
                result.getDesc(), result.getQipuId(), result.getStartPlayTime(), result.getOrder());

        return result;
    }

    private List<ProgramDataExpired> getExpiredPrograms(ChannelBase channel){
        List<ProgramDataExpired> result = new ArrayList<>();
        JSONObject dataEntity = ApiQipuUtil.rpcReadQipuEntity(channel.getQipuId());
        if(null == dataEntity){
            return result;
        }

        JSONArray programList = dataEntity.getJSONArray(LIVE_EPISODE_SUMMARY);
        if(null == programList || programList.size()==0){
            return result;
        }

        Iterator<Object> pt = programList.iterator();
        JSONObject programEntity = null;
        ProgramDataExpired program = null;
        while(pt.hasNext()){
            programEntity = (JSONObject) pt.next();
            // TODO change to 30 days
            if(programEntity.getLongValue(PROGRAM_START_PLAY_TIME) == 0){
                continue;
            }

            if(programEntity.getLongValue(PROGRAM_START_PLAY_TIME) <= getPreviousNDays(30)){
                result.add(transferProgram(channel, programEntity));
            }
        }

        return result;
    }

    private void deleteProgramToQipu(ProgramDataExpired program) {
        qipuManager.setObjectDeletedToQipu(program.getQipuId(), true);
    }

    private void deleteProgramInChannelQipu(ProgramDataExpired program) {
        TProgram p = new TProgram();
        p.setPname(program.getDesc());
        p.setId(program.getOrder());
        p.setAddTime("2015-11-11 11:11:11");
        qipuManager.setLiveEpisodeFatherInfoToQipu(program.getQipuId(), p, program.getFatherQipuId(), false);
    }

    private class FetchingExecutor implements Runnable {
        @Override
        public void run(){
            ChannelBase entity;
           while (true) {
               entity = null;
               synchronized (fetchingQueue) {
                   if(!fetchingQueue.isEmpty()){
                       entity = fetchingQueue.poll();
                   }
               }

               if(null == entity || state == STATE_FORCESTOP){
                   break;
               }

               List<ProgramDataExpired> programList = getExpiredPrograms(entity);
               programService.insertExpiredProgramList(programList);
           }
        }
    }

    private class DeleteExecutor implements Runnable {
        long startTaskTime;
        @Override
        public void run(){
            ProgramDataExpired entity;
            while (true) {
                entity = null;
                synchronized (deleteQueue) {
                    if(!deleteQueue.isEmpty()){
                        entity = deleteQueue.poll();
                    }
                }

                if(null == entity || state == STATE_FORCESTOP){
                    break;
                }

                startTaskTime = System.currentTimeMillis();
                logger.info("[ProgramTempRecycleTrigger][DeleteExecutor][start at {}]", startTaskTime);
                deleteProgramToQipu(entity);
                deleteProgramInChannelQipu(entity);
                programService.updateDeletedExpiredProgram(entity);
                logger.info("[ProgramTempRecycleTrigger][DeleteExecutor][end cost {}]", System.currentTimeMillis() -startTaskTime);
            }
        }
    }

//    public static void main(String[] args) {
//        ProgramTempRecycleTrigger t = new ProgramTempRecycleTrigger();
//        ChannelBase channelBase = new ChannelBase();
//        channelBase.setId(414L);
//        channelBase.setQipuId(180114622L);

//        List<ProgramDataExpired> programList = t.getExpiredPrograms(channelBase);
//    }
}
